/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package main.DistributedResourceMgmt;

/**
 *
 * @author Isaac Osesina
 */
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.ggf.drmaa.DrmaaException;
import org.ggf.drmaa.Session;
import org.ggf.drmaa.SessionFactory;

public class DRMAA_Manager implements Runnable {

    private static DRMAA_Manager singleton = null;
    private static Session session;
    private Object lock = new Object();
    private Map<String, Integer> jobID_Status;
    private Map<String, String> jobID_ActorID;

    private DRMAA_Manager() {
        SessionFactory factory = SessionFactory.getFactory();
        session = factory.getSession();        
        jobID_Status = new ConcurrentHashMap<String, Integer>();
        jobID_ActorID = new ConcurrentHashMap<String, String>();
        singleton.run();
    }

    public static DRMAA_Manager getSingleton() {
        if (session == null) {
            singleton = new DRMAA_Manager();
        }
        return singleton;
    }

    public Session getSession() {
        return session;
    }

    public void initiateSession() {
        try {
            session.init("");
        } catch (DrmaaException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }

    public void exitSession() {
        try {
            session.exit();
        } catch (DrmaaException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
    
    /**
     * ???Determine how scripts for Actor operation should be created
     * Might need to keep a third Map<actorId, Actor>
     * Actor should wait until notified by DRMAA_Manager
     * @return id of job on queue manager
     */
    public synchronized String submitJob() throws DrmaaException {
        
        
        
        String jobId = session.runJob(null);        
        waitForJob(jobId);
        return jobId;
    }

    /**
     * Adds job Id from session.runJob(***) invoke is to map containing
     * jobs being waited on
     * @param jobId id of job on queue manager
     */
    public synchronized void waitForJob(String jobId) {
        if (jobId == null) {
            return;
        }
        jobID_Status.put(jobId, Session.QUEUED_ACTIVE);
        synchronized (lock) {
            lock.notify();
        }
    }

    /**
     * Returns the status of the job identified by parameter
     * Status is obtained from session.getJobProgramStatus;
     * @param jobId id of job on queue manager
     * @return status of the job
     */
    public synchronized Integer getJobStatus(String jobId) {
        if (jobId == null) {
            return Integer.MIN_VALUE;
        }
        return jobID_Status.get(jobId);
    }

    /**
     * Returns the id of actor which submitted this job to DRMAA_Manager
     * @param jobId id of job on queue manager
     * @return id of actor corresponding to jobId
     */
    public synchronized String getActorId(String jobId) {
        if (jobId == null || jobID_ActorID == null) {
            return null;
        }
        return jobID_ActorID.get(jobId);
    }

    @Override
    public void run() {
        while (true) {
            if (jobID_Status != null && !jobID_Status.isEmpty()) {
                Iterator<String> it = jobID_Status.keySet().iterator();
                while (it.hasNext()) {
                    String jobId = it.next();
                    if (jobId == null) {
                        jobID_Status.remove(jobId);
                        continue;
                    }
                    Integer status = this.getJobStatus(jobId);
                    switch (status) {
                        case Session.DONE:
                            System.out.println(jobId + "Job finished normally\n");
                            jobID_Status.remove(jobId);
                            String actorId = this.getActorId(jobId);
                            //getActor
                            //notifyActor
                            break;
                        case Session.UNDETERMINED:
                            System.out.println(jobId + "Job status cannot be determined\n");
                            jobID_Status.remove(jobId);
                            //rerun job?
                            break;
                        case Session.FAILED:
                            System.out.println(jobId + "Job finished, but failed\n");
                            jobID_Status.remove(jobId);
                            //rerun job?
                            break;
                        default:
                            jobID_Status.put(jobId, status);
                    }
                }
            } else {
                synchronized (lock) {
                    try {
                        lock.wait();
                    } catch (InterruptedException ex) {
                        Logger.getLogger(DRMAA_Manager.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }

        }
    }
}
